"""
Gateway for hft
"""
import os
import sys
import asyncio
import time
from collections import defaultdict

VERSION = ''
with open('/inc/version.inc') as f:
    lines = f.readlines()
    VERSION = lines[0].split('=')[1].strip()
sys.path.append('/opt/version/' + VERSION)
sys.path.append('/opt/version/' + VERSION + '/python-libs')

from data_struct import *

from nano_object import (
    InstrumentInfo,
    OrderTrade,
    Balance,
    Position,
)

from databox.pydatabox import DataBox
from securitymanager.pysecuritymanager import SecurityManager as Smc
from log import *
import traceback
from command_helper import TradeClient
import multiprocessing as mp
from nanomsg_util import NanomsgSuber

import pandas as pd

from vn_local.trader.gateway import BaseGateway
from vn_local.trader.constant import (
    BinanceContractType, Direction,
    Exchange, Product,
    Status, OrderType,
    Interval, Offset, ApiSource
)
from vn_local.trader.object import (
    LogData,
    TickData,
    OrderTradeData,
    AccountData,
    PublicTradeData,
    BinanceContractData,
    PositionData,
    BarData,
    QuoteData,
    OrderRequest,
    CancelRequest,
    AmendRequest,
    SubscribeRequest,
    HistoryRequest,
    QuoteRequest,
    ContractData,
    CancelAllRequest,
    QueryOpenOrderRequest
)
import logging
from vn_local.trader.util_logger import setup_logger
from vn_local.trader.utility import get_folder_path, virtual, get_file_path, Record
from vn_local.event import Event, EventEngine
from vn_local.trader.event import (
    EVENT_BAR,
    EVENT_FUND_RATE_DATA,
    EVENT_QUOTE,
    EVENT_TICK,
    EVENT_ORDER,
    EVENT_TRADE,
    EVENT_POSITION,
    EVENT_ACCOUNT,
    EVENT_CONTRACT,
    EVENT_LOG
)

from typing import Any, Callable, Dict, List, Tuple, Optional
from copy import copy
from threading import Thread
from datetime import datetime
from vn_local.trader.event import EVENT_LOG
from dataclasses import dataclass
from dataclasses_json import dataclass_json

# 币安合约类型映射
# 合约类型映射
# ContractType_VT2HFT: Dict[BinanceContractType, str] = {
#     BinanceContractType.PERPETUAL: "InstType_USDT_SWAP",
#     BinanceContractType.CURRENT_MONTH: "InstType_USDT_FUTURES",
#     BinanceContractType.NEXT_MONTH: "InstType_USDT_FUTURES",
#     BinanceContractType.CURRENT_QUARTER: "InstType_USDT_FUTURES",
#     BinanceContractType.NEXT_QUARTER: "InstType_USDT_FUTURES",
#     BinanceContractType.CURRENT_QUARTER_DELIVERING: "InstType_USDT_FUTURES",
#     BinanceContractType.NEXT_QUARTER_DELIVERING: "InstType_USDT_FUTURES",
#     BinanceContractType.CURRENT_MONTH_DELIVERING: "InstType_USDT_FUTURES",
#     BinanceContractType.NEXT_MONTH_DELIVERING: "InstType_USDT_FUTURES"
# }
ContractType_HFT2VT: Dict[str, BinanceContractType] = {
    "USDT_SWAP": BinanceContractType.PERPETUAL
}
# InstType映射(smc需要str，下单需要InstType)
InstType_STR2HFT: Dict[str, InstType] = {
    "SPOT": SPOT,
    "USDT_SWAP": USDT_SWAP,
    "C_SWAP": C_SWAP,
    "USDT_FUTURES": USDT_FUTURES,
    "C_FUTURES": C_FUTURES
}
InstType_HFT2STR: Dict = {v: k for k, v in InstType_STR2HFT.items()}
# 交易所类型映射
Exchange_VT2HFT = {
    Exchange.OKX: OKX,
    Exchange.BINANCE: BINANCE,
    Exchange.BYBIT: BYBIT,
    Exchange.GATEIO: GATEIO,
    Exchange.BTSE: BTSE
}
Exchange_HFT2VT = {v: k for k, v in Exchange_VT2HFT.items()}
# Offset类型映射
Offset_VT2HFT = {
    Offset.OPEN: OffsetFlag_OPEN,
    Offset.CLOSE: OffsetFlag_CLOSE
}
Offset_HFT2VT = {v: k for k, v in Offset_VT2HFT.items()}
# Direction类型映射
Direction_VT2HFT = {
    Direction.LONG: Direction_LONG,
    Direction.SHORT: Direction_SHORT,
    Direction.NET: Direction_NET
}
Direction_HFT2VT = {v: k for k, v in Direction_VT2HFT.items()}
Pos_Adj_HFT2VT = {
    Direction_SHORT: -1,
    Direction_LONG: 1
}
# Ordertype类型映射
OrderType_VT2HFT = {
    OrderType.LIMIT: OrderType_LIMIT,
    OrderType.MARKET: OrderType_MARKET,
    OrderType.MAKER: OrderType_POST_ONLY,
    OrderType.FOK: OrderType_FOK,
    OrderType.FAK: OrderType_IOC,
}
OrderType_HFT2VT = {v: k for k, v in OrderType_VT2HFT.items()}

# Product类型映射
ProductType_VT2HFT = {
    Product.SPOT: InstType.SPOT,
    Product.USWAP: InstType.USDT_SWAP
}
ProductType_HFT2VT = {v: k for k, v in ProductType_VT2HFT.items()}

# Status类型映射
Status_HFT2VT = {
    OrderStatus_PENDING_NEW: Status.PENDING,
    OrderStatus_NEW: Status.NOTTRADED,
    OrderStatus_PARTFILLED: Status.PARTTRADED,
    OrderStatus_FILLED: Status.ALLTRADED,
    OrderStatus_REJECTED: Status.REJECTED,
    OrderStatus_CANCELLING: Status.CANCELLING,
    OrderStatus_CANCELED: Status.CANCELLED,
    OrderStatus_UNKNOWN: Status.UNKNOWN,
    OrderStatus_FAILED: Status.FAILED
}

# APIsource类型映射
ApiSource_HFT2VT = {
    ApiSource_ADD_NEW_ORDER: ApiSource.SEND,
    ApiSource_CANCEL_ORDER: ApiSource.CANCEL,
    ApiSource_QUERY_ORDER: ApiSource.QUERY,
    ApiSource_REST: ApiSource.REST,
    ApiSource_WEBSOCKET: ApiSource.WEBSOCKET,
    ApiSource_MIN: ApiSource.ERROR,
    ApiSource_MAX: ApiSource.ERROR

}
# InstTypeStrAndEnumMap = {
#     "InstType_SPOT": InstType_SPOT,
#     "InstType_MARGIN": InstType_MARGIN,
#     "InstType_USDT_SWAP" : InstType_USDT_SWAP,
#     "InstType_BUSD_SWAP" : InstType_BUSD_SWAP,
#     "InstType_C_SWAP" : InstType_C_SWAP,
#     "InstType_USDT_FUTURES" : InstType_USDT_FUTURES,
#     "InstType_C_FUTURES" : InstType_C_FUTURES,
#
#     InstType_SPOT : "InstType_SPOT",
#     InstType_USDT_SWAP : "InstType_USDT_SWAP",
#     InstType_BUSD_SWAP : "InstType_BUSD_SWAP",
#     InstType_C_SWAP : "InstType_C_SWAP",
#     InstType_USDT_FUTURES : "InstType_USDT_FUTURES",
#     InstType_C_FUTURES : "InstType_C_FUTURES" ,
# }
#
# MarketTypeStrAndEnumMap = {
#     "TRADES" : TRADES,
#     "FUNDING_RATE" :FUNDING_RATE,
#     "DEPTH1" : DEPTH1,
#     "DEPTH5" : DEPTH5,
#     "DEPTH10" : DEPTH10,
#     "DEPTH20" : DEPTH20,
#     "KLINE_1m": KLINE_1m,
#     "MBP" : MBPType,
#
#     TRADES : "TRADES",
#     FUNDING_RATE : "FUNDING_RATE" ,
#     DEPTH1 : "DEPTH1",
#     DEPTH5 : "DEPTH5",
#     DEPTH10 : "DEPTH10",
#     DEPTH20 : "DEPTH20",
#     KLINE_1m : "KLINE_1m",
#     MBPType : "MBP",
# }

# 合约数据全局缓存字典
symbol_name_map: Dict[str, str] = {}

# binance_symbol_instId_map: Dict[str, str] = {}
# gate_symbol_instId_map: Dict[str, str] = {}

# symbol_instId_map: Dict[str, str] = {}
# origin_hft_instId_map: Dict[str, str] = {}
# vt_symbol_origin_map: Dict[str, int] = {}  # vt_symbol(.exchange)
vt_symbol_origin_map: Dict[str, Tuple] = {}
contract_type_map: Dict[str, BinanceContractType] = {}
margin_asset_map: Dict[str, str] = {}
last_price_map: Dict[str, float] = {}
contract_map: Dict[str, BinanceContractData] = {}


class HFTConverter:
    """
    回调接受的结构体转成vt结构体
    transfer python-lib/object/object/Order to OrderData
    """

    def __init__(self, gateway_name: str = 'HFT'):
        self.gateway_name = gateway_name

    def convert_binance_contract(self, hft_contract: InstrumentInfo) -> BinanceContractData:
        # raw_json = json.loads(hft_contract.rawJson)
        contract = BinanceContractData(
            symbol=hft_contract.instId,
            exchange=Exchange.BINANCE,
            # name=hft_contract.vids,
            name=(hft_contract.exchId, hft_contract.instType, hft_contract.instId),
            pricetick=hft_contract.tickSize,
            size=20,  # binance 默认为20倍杠杆
            min_volume=hft_contract.lotSize,
            min_notional=hft_contract.minSize,
            max_market_size=hft_contract.marketMaxSize,
            base=hft_contract.base,  # 适配高频
            quote=hft_contract.quote,  # 适配高频
            product=Product.FUTURES,
            history_data=True,
            margin_rate=1,
            stop_supported=True,
            market_supported=True,
            net_position=False,
            margin_asset=hft_contract.margin,
            contract_type=ContractType_HFT2VT[hft_contract.instType],
            gateway_name=self.gateway_name
        )
        return contract

    def convert_bybit_contract(self, hft_contract: InstrumentInfo) -> ContractData:
        contract = ContractData(
            symbol=hft_contract.instId,
            exchange=Exchange.BYBIT,
            # name=hft_contract.vids,
            name=(hft_contract.exchId, hft_contract.instType, hft_contract.instId),
            base=hft_contract.base,  # 适配高频
            quote=hft_contract.quote,  # 适配高频
            pricetick=hft_contract.tickSize,
            size=20,
            min_volume=hft_contract.lotSize,
            product=Product.FUTURES,
            gateway_name=self.gateway_name,
            history_data=True
        )
        return contract

    def convert_gateio_contract(self, hft_contract: InstrumentInfo) -> ContractData:
        contract = ContractData(
            symbol=hft_contract.instId,
            exchange=Exchange.GATEIO,
            # name=hft_contract.vids,
            name=(hft_contract.exchId, hft_contract.instType, hft_contract.instId),
            base=hft_contract.base,  # 适配高频
            quote=hft_contract.quote,  # 适配高频
            pricetick=hft_contract.tickSize,
            size=hft_contract.value,  # 1张表示多少个币
            min_volume=hft_contract.minSize,
            product=Product.FUTURES,
            gateway_name=self.gateway_name,
            history_data=True
        )
        return contract

    def convert_btse_contract(self, hft_contract: InstrumentInfo) -> ContractData:
        contract = ContractData(
            symbol=hft_contract.instId,
            exchange=Exchange.BTSE,
            # name=hft_contract.vids,
            name=(hft_contract.exchId, hft_contract.instType, hft_contract.instId),
            base=hft_contract.base,  # 适配高频
            quote=hft_contract.quote,  # 适配高频
            pricetick=hft_contract.tickSize,
            size=hft_contract.value,  # 1张表示多少个币
            min_volume=hft_contract.lotSize,
            product=Product.SPOT if hft_contract.instType == "SPOT" else Product.FUTURES,
            gateway_name=self.gateway_name,
            history_data=True
        )
        return contract

    def convert_okx_contract(self, hft_contract: InstrumentInfo) -> ContractData:
        contract = ContractData(
            symbol=hft_contract.instId,
            exchange=Exchange.OKX,
            # name=hft_contract.vids,
            name=(hft_contract.exchId, hft_contract.instType, hft_contract.instId),
            base=hft_contract.base,  # 适配高频
            quote=hft_contract.quote,  # 适配高频
            pricetick=hft_contract.tickSize,
            size=hft_contract.value,  # 1张表示多少个币
            min_volume=hft_contract.minSize,
            product=Product.FUTURES,
            gateway_name=self.gateway_name,
            history_data=True
        )
        return contract

    def convert_account(self, hft_balance: Balance) -> AccountData:
        account = AccountData(
            exchange=Exchange_HFT2VT[hft_balance.exchangeTypeEnum],
            currency=hft_balance.currency,
            available=hft_balance.available,
            frozen=hft_balance.frozen,
            pnl=hft_balance.unrealizedPnl,
            total=hft_balance.total,
            update_time=hft_balance.updateTime,
            api_source=ApiSource_HFT2VT[hft_balance.apiSourceEnum],
            gateway_name=self.gateway_name
        )
        return account

    def convert_position(self, hft_position: Position) -> PositionData:
        position = PositionData(
            symbol=hft_position.instId,
            exchange=Exchange_HFT2VT[hft_position.exchangeTypeEnum],
            direction=Direction_HFT2VT[hft_position.direction],
            volume=Pos_Adj_HFT2VT[hft_position.direction] * hft_position.volume,
            frozen=hft_position.maintMargin,
            price=hft_position.avgPrice,
            pnl=hft_position.unrealizedPnl,
            liq_price=hft_position.liquidPrice,
            mark_price=hft_position.markPrice,
            api_source=ApiSource_HFT2VT[hft_position.apiSourceEnum],
            adl=hft_position.adlQuantile,
            update_time=hft_position.updateTime,
            gateway_name=self.gateway_name
        )
        return position

    def convert_order_trade(self, hft_order_trade: OrderTrade) -> OrderTradeData:
        order_trade = OrderTradeData(
            symbol=hft_order_trade.instId,
            exchange=Exchange_HFT2VT[hft_order_trade.exchangeTypeEnum],
            orderid=str(hft_order_trade.clientOrderId),  # # 只有自定义的订单号返回
            type=OrderType_HFT2VT[hft_order_trade.orderType],
            direction=Direction_HFT2VT[hft_order_trade.direction],
            offset=Offset_HFT2VT[hft_order_trade.offsetFlag],
            price=hft_order_trade.limitPrice,
            volume=hft_order_trade.volumeTotal,
            traded=hft_order_trade.volumeTraded,
            trade_price=hft_order_trade.tradePrice,
            trade_diff=hft_order_trade.tradedDiff,
            status=Status_HFT2VT[hft_order_trade.orderStatus],
            insert=hft_order_trade.insertTime,
            update=hft_order_trade.updateTime,
            api_source=ApiSource_HFT2VT[hft_order_trade.apiSourceEnum],
            is_maker=hft_order_trade.isMaker,
            reduce_only=hft_order_trade.reduceOnly,
            error_id=hft_order_trade.ErrorID,
            error_msg=hft_order_trade.originMsg,
            reference=hft_order_trade.strategyRef,
            gateway_name=self.gateway_name
        )
        return order_trade


class DataProcess(mp.Process):
    pass


def if_account_recorded(account_record_path, record_date):
    df = pd.read_csv(account_record_path)

    if record_date in list(df.date):
        return True
    else:
        return False


def generate_order_id():
    return str(time.time_ns())


class HftGateway(BaseGateway):
    """
    Run as BaseGateway
    """

    default_setting: Dict[str, Any] = {
        'redis_host': "",
        'redis_port': 0,
        'redis_password': "",
        'oms_tcmd': "",
        'oms_rcmd': "",
        'inst_type': 'InstType_USDT_SWAP',
        'subscribe_codes': [],
        'balance_codes': [],
        'strategyId': "",
        'account_path': ""
    }
    exchanges: List[Exchange] = [Exchange.BTSE]

    def __init__(self, event_engine: EventEngine):
        # Constructor
        super().__init__(event_engine, gateway_name="HFT")

        self.hft_thread: Thread = Thread(target=self.run)
        # mktdata
        self.ticks: Dict[int, TickData] = {}
        self.public_trades: Dict[int, PublicTradeData] = {}
        self.replace_times: Dict[int, int] = defaultdict(int)
        self.replace_limit = 3

        # 监控行情计时器
        # self.count_init = False
        self.init_time = time.time()
        self.last_monitor_time = time.time()
        self.send_count = 0
        self.receive_count = 0
        self.effective_count = 0
        self.effective_net_delay_tolerant = 75500
        self.effective_dequee_delay_tolerant = 1500

    def connect(self, setting: Dict) -> None:
        # hft参数
        redis_host = setting["redis_host"]
        redis_port = setting["redis_port"]
        redis_password = setting["redis_password"]
        oms_tcmd = setting["oms_tcmd"]
        oms_rcmd = setting["oms_rcmd"]
        self.strategyid = setting['strategyId']
        self.subscribe_codes = setting["subscribe_codes"]
        self.timer_interval = setting['timer_interval']
        self.balance_codes = setting["balance_codes"]

        # self.account_path = os.path.abspath(setting['account_record_path'])

        self.databox = DataBox(redis_host, redis_port, redis_password)
        self.smc = Smc(redis_host, redis_port, redis_password, True)

        self.nanomsg_client = NanomsgSuber(oms_rcmd)
        self.tradeClient = TradeClient(oms_tcmd)

        self.coin_list = list(set([x.split('.')[2] for x in self.subscribe_codes]))
        # print(self.coin_list)
        self.inst_type_str = "SPOT"
        self.inst_type = InstType_STR2HFT[self.inst_type_str]
        # tick深度
        self.tick_len = 1

        # loop
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(self.loop)
        self.mdQueue = asyncio.Queue()

        # 下单/持仓/账户 转换器
        self.gw_converter = HFTConverter()

        # # 账户记录
        self.account_path = setting["account_path"]
        self.Account_R = Record(self.account_path)
        self.account_csv_header = ['date', 'binance', "btse"]
        self.binance_account = 0
        self.gate_account = 0
        self.bybit_account = 0
        self.btse_account = 0
        self.record_timer = 0
        # self.account_recorded = if_account_recorded(self.account_path, str(datetime.now().date()))
        self.monitor_count = 0

        # init_query
        self.query_contract()
        # self.query_position()
        self.query_account()
        # # 启动线程
        self.hft_thread.start()

    def run(self):
        try:
            self.databox.register_on_md(self.__on_md)
            for code in self.subscribe_codes:
                self.databox.subscribe(code)

            # oms
            self.loop.create_task(self.__handle_rcmd())
            # 定时任务
            # self.loop.create_task(self.monitor())
            self.loop.create_task(self.__timer())
            self.loop.run_forever()

        except Exception as e:
            traceback.print_exc()
            log = LogData(gateway_name="hft", msg=traceback.format_exc(), prefix="hft")
            self.on_log(log)

    def query_account(self) -> None:
        for exchange in self.exchanges:
            self.tradeClient.query_account(Exchange_VT2HFT[exchange], self.inst_type, self.strategyid)

    def query_position(self) -> None:
        for exchange in self.exchanges:
            for coin in self.coin_list:
                # print(f"开始查询{coin}持仓")
                self.tradeClient.query_position(Exchange_VT2HFT[exchange], self.inst_type, self.strategyid, coin)

    def __on_md(self, channel, md):
        try:
            # print(md)
            if md['instId'] not in self.coin_list:
                return
            if self.event_engine._queue.qsize() > 5000:
                return
            self.send_count += 1
            self.on_marketdata(md)
        except Exception as e:
            traceback.print_exc()
            log = LogData(gateway_name="hft", msg=traceback.format_exc(), level=40, prefix="hft")
            self.on_log(log)

    async def __handle_rcmd(self):
        suberSock = self.nanomsg_client.get_suber()
        while True:
            try:
                msg = await suberSock.arecv_msg()

                try:
                    rcmd = json.loads(msg.bytes.decode(), strict=False)
                    otok, orderTrade = TradeClient.convert_rcmd_2_ordertrade(rcmd)
                    if otok:
                        self.on_hft_orderTrade(orderTrade)
                        continue
                    pok, position = TradeClient.convert_rcmd_2_position(rcmd)
                    if pok:
                        self.on_hft_position(position)
                        continue
                    bok, balance = TradeClient.convert_rcmd_2_balance(rcmd)
                    if bok:
                        self.on_hft_balance(balance)
                        continue
                except Exception as e:
                    log = LogData(gateway_name="hft", msg=msg.bytes.decode(), level=40, prefix="hft")
                    self.on_log(log)
                    log = LogData(gateway_name="hft", msg=traceback.format_exc(), level=40, prefix="hft")
                    self.on_log(log)
            except Exception as e:
                traceback.print_exc()
                log = LogData(gateway_name="hft", msg=traceback.format_exc(), level=40, prefix="hft")
                self.on_log(log)

    async def __timer(self):
        while True:
            try:
                now = int(time.time())
                # print(f"{time.time()}:进入__timer")
                self.on_hft_timer(now)
                await asyncio.sleep(0.001)
            except Exception as e:
                log = LogData(gateway_name="hft", msg=traceback.format_exc(), level=40, prefix="hft")
                self.on_log(log)

    def on_hft_timer(self, now):
        try:
            time_diff = now - self.last_monitor_time
            if time_diff < 1:
                return
            self.on_timer(now)
            self.last_monitor_time = now
            self.monitor_count += 1
            if self.monitor_count >= 100:
                # print("进入timer")
                self.write_log(
                    f"{time.time()} {self.coin_list[0]} md queue left size: {self.mdQueue.qsize()} " \
                    f"sendCount: {self.send_count}, receive_Count: {self.receive_count}, low-latencyCount: {self.effective_count} " \
                    f"event queue left size: {self.event_engine._queue.qsize()}")
                self.monitor_count = 0
        except Exception as e:
            log = LogData(gateway_name="hft", msg=traceback.format_exc(), level=40, prefix="hft")
            self.on_log(log)

    # async def monitor(self):
    #     while True:
    #         try:
    #             t_now = time.time()
    #             if t_now - self.last_monitor_time >= 10:
    #                 self.last_monitor_time = t_now
    #                 # if self.count_init:
    #                 print(
    #                     f"{time.time()} {self.coin_list[0]} md queue left size: {self.mdQueue.qsize()} " \
    #                     f"sendCount: {self.send_count}, receive_Count: {self.receive_count}, low-latencyCount: {self.effective_count} " \
    #                     f"event queue left size: {self.event_engine._queue.qsize()}")
    #                 self.record_timer += 1
    #                 if self.record_timer >= 100:
    #                     self.record_timer = 0
    #
    #         except Exception as e:
    #             print(traceback.format_exc())
    #         finally:
    #             await asyncio.sleep(0.0001)

    def close(self) -> None:
        pass

    def subscribe(self, req: SubscribeRequest):
        """only use for create tick buf data in this case"""
        if req.vt_symbol not in vt_symbol_origin_map:
            # # 保证查询到了contract
            self.write_log(f"找不到该合约代码{req.vt_symbol}")
            return
        # print("subscribe",req.vt_symbol)
        tick = TickData(
            symbol=req.symbol,
            name=vt_symbol_origin_map.get(req.vt_symbol, ""),
            exchange=req.exchange,
            datetime=datetime.now(),
            gateway_name=self.gateway_name,
        )

        public_trade = PublicTradeData(
            symbol=req.symbol,
            name=vt_symbol_origin_map.get(req.vt_symbol, ""),
            exchange=req.exchange,
            datetime=datetime.now(),
            gateway_name=self.gateway_name,
        )
        # print(tick)
        self.ticks[tick.name] = tick
        self.public_trades[tick.name] = public_trade
        # print(tick)
        # print(public_trade)

    def query_contract(self):
        """"""
        for exchange in self.exchanges:
            for instrument in self.coin_list:
                if instrument == "BTSE-ETH":
                    # contract = ContractData(symbol=instrument, exchange=Exchange.UNISWAP, name=0, pricetick=0.000001,
                    #                         size=1, product=Product.SPOT, gateway_name=self.gateway_name)
                    contract = ContractData(symbol=instrument, exchange=Exchange.UNISWAP,
                                            name=("UNISWAP", "SPOT", "BTSE-ETH"), pricetick=0.000001,
                                            size=1, product=Product.SPOT, gateway_name=self.gateway_name)
                    vt_symbol_origin_map[contract.vt_symbol] = contract.name
                    self.on_contract(contract)
                else:
                    ok, smc_info = self.smc.get_instrument_info(exchange.value, self.inst_type_str, instrument)

                    if not ok:
                        return
                    # change to dataclass
                    instrument_info = InstrumentInfo.from_dict(smc_info)
                    if exchange == Exchange.BINANCE:
                        # convert
                        contract = self.gw_converter.convert_binance_contract(instrument_info)
                        vt_symbol_origin_map[contract.vt_symbol] = contract.name
                        self.on_contract(contract)

                    if exchange == Exchange.BYBIT:
                        contract = self.gw_converter.convert_bybit_contract(instrument_info)
                        vt_symbol_origin_map[contract.vt_symbol] = contract.name
                        self.on_contract(contract)

                    if exchange == Exchange.GATEIO:
                        contract = self.gw_converter.convert_gateio_contract(instrument_info)
                        vt_symbol_origin_map[contract.vt_symbol] = contract.name
                        self.on_contract(contract)

                    if exchange == Exchange.OKX:
                        contract = self.gw_converter.convert_okx_contract(instrument_info)
                        vt_symbol_origin_map[contract.vt_symbol] = contract.name
                        self.on_contract(contract)

                    if exchange == Exchange.BTSE:
                        contract = self.gw_converter.convert_btse_contract(instrument_info)
                        # print(contract)
                        # vt_symbol_origin_map[contract.vt_symbol] = contract.name
                        vt_symbol_origin_map[contract.vt_symbol] = contract.name

                        self.on_contract(contract)

            self.write_log(f"{exchange.value}合约信息查询成功")

    def on_hft_public_trades(self, md):
        # print(f"{md}")
        # public_trade = self.public_trades.get(md["vids"], None)
        # public_trade = self.public_trades.get(md["vids"], None)
        public_trade = self.public_trades.get((md["exchId"], md["instType"], md["instId"]), None)

        if not public_trade:
            return

        if md["ts"] < public_trade.ts:
            return

        public_trade.volume = md["sz"]
        public_trade.price = md["px"]
        public_trade.direction = Direction.SHORT if md["side"] == "sell" else Direction.LONG

        public_trade.ts = md['ts']
        public_trade.tsNet = md['tsNet']
        public_trade.tsParse = md['tsParse']
        public_trade.tsGateway = int(time.time() * 1e6)
        # print(public_trade)
        self.on_public_trade(public_trade)
        # print(f"on_gateway {md}")

    def on_marketdata(self, md):
        # print(md)
        # print("pos0")
        if md["marketType"] == "TRADES":
            self.on_hft_public_trades(md)
            return
        # tick = self.ticks.get(md["vids"], None)
        tick = self.ticks.get((md["exchId"], md["instType"], md["instId"]), None)
        # 初始化过滤
        if not tick:
            """not subscribed or query contract yet"""
            return
        # print("pos1")
        # 行情乱序过滤
        if md['ts'] < tick.ts:
            return
        # print("pos2")
        last_tick = copy(tick)
        bids = md['bidsList']
        for n in range(self.tick_len):
            price, volume = bids[n]
            tick.__setattr__("bid_price_" + str(n + 1), float(price))
            tick.__setattr__("bid_volume_" + str(n + 1), float(volume))

        asks = md['asksList']
        for n in range(self.tick_len):
            price, volume = asks[n]
            tick.__setattr__("ask_price_" + str(n + 1), float(price))
            tick.__setattr__("ask_volume_" + str(n + 1), float(volume))

        tick.ts = md['ts']
        tick.tsNet = md['tsNet']
        tick.tsParse = md['tsParse']
        tick.tsGateway = int(time.time() * 1e6)
        # tick.datetime = datetime.fromtimestamp(now / 1000000)
        # print("pos3")
        change = False
        for n in range(self.tick_len):
            if last_tick.__getattribute__(f"bid_price_{(n + 1)}") != tick.__getattribute__(
                    f"bid_price_{(n + 1)}"):
                change = True
                break
            if last_tick.__getattribute__(f"bid_volume_{(n + 1)}") != tick.__getattribute__(
                    f"bid_volume_{(n + 1)}"):
                change = True
                break
            if last_tick.__getattribute__(f"ask_price_{(n + 1)}") != tick.__getattribute__(
                    f"ask_price_{(n + 1)}"):
                change = True
                break
            if last_tick.__getattribute__(f"ask_volume_{(n + 1)}") != tick.__getattribute__(
                    f"ask_volume_{(n + 1)}"):
                change = True
                break

        if change:
            # print("pos4")
            # if self.count_init:
            #     print('here')
            self.receive_count += 1
            # print("gateway on tick")
            self.on_tick(copy(tick))
            # print("gateway_tick", tick.vt_symbol, tick.ask_price_1)
        # print(tick)
        if (tick.tsNet - tick.ts > self.effective_net_delay_tolerant) or (
                tick.tsGateway - tick.tsNet > self.effective_dequee_delay_tolerant):
            return
        self.effective_count += 1

    def replace_order(self, hft_orderTrade: OrderTrade) -> None:
        """
        直接从hft_order_action获取订单信息追单
        """
        msg = f"[replaceOrder]: 开始第{self.replace_times[hft_orderTrade.clientOrderId]}次追单" \
              f"状态码：{hft_orderTrade.ErrorID}, 信息：{hft_orderTrade.originMsg}, " \
              f"订单号：{hft_orderTrade.clientOrderId}, 交易所订单号：{hft_orderTrade.orderId}"
        self.write_log(msg)
        self.tradeClient.add_new_order(hft_orderTrade.exchangeTypeEnum, self.inst_type, hft_orderTrade.strategyId,
                                       hft_orderTrade.instId,
                                       hft_orderTrade.offsetFlag, hft_orderTrade.direction,
                                       hft_orderTrade.orderType,
                                       hft_orderTrade.limitPrice, hft_orderTrade.volumeTotal,
                                       hft_orderTrade.clientOrderId, hft_orderTrade.reduceOnly)

    def send_order(self, req: OrderRequest) -> str:
        # print('enter gateway send order')
        orderid = generate_order_id()
        order = req.create_order_data(
            orderid,
            self.gateway_name
        )
        # print(Exchange_VT2HFT[req.exchange], self.inst_type, self.strategyid,
        #                                req.symbol,
        #                                Offset_VT2HFT[req.offset], Direction_VT2HFT[req.direction],
        #                                OrderType_VT2HFT[req.type],
        #                                req.price, req.volume, int(orderid), req.reduce_only, req.reference)

        self.tradeClient.add_new_order(Exchange_VT2HFT[req.exchange], self.inst_type, self.strategyid,
                                       req.symbol,
                                       Offset_VT2HFT[req.offset], Direction_VT2HFT[req.direction],
                                       OrderType_VT2HFT[req.type],
                                       req.price, req.volume, int(orderid), req.reduce_only, req.reference)
        return order.vt_orderid

    def amend_order(self, req: AmendRequest) -> None:
        self.tradeClient.amend_order(Exchange_VT2HFT[req.exchange], self.inst_type, self.strategyid, req.symbol,
                                     req.price, req.volume, req.orderid)

    def cancel_order(self, req: CancelRequest):
        # print("enter gateway cancel order")
        self.tradeClient.cancel_order(Exchange_VT2HFT[req.exchange], self.inst_type, self.strategyid,
                                      req.symbol, int(req.orderid))

    def query_order(self, req: CancelRequest):
        self.tradeClient.query_order(Exchange_VT2HFT[req.exchange], self.inst_type, self.strategyid,
                                     req.symbol, int(req.orderid))

    def get_funding_rate(self, req: SubscribeRequest) -> str:
        ok, swapFundingKeyI, swapFundingData = self.databox.get_md(req.exchange.value, self.inst_type_str,
                                                                   "FUNDING_RATE", req.symbol)
        # print(swapFundingData['fundingRate'])
        return swapFundingData['fundingRate']

    def get_spot_order_book(self, symbol: str, exchange: str) -> Tuple:
        # print("get_spot_order_book", symbol,exchange)
        ok, key, data = self.databox.get_md(exchange, self.inst_type_str, "DEPTH20", symbol)
        return data["asksList"], data["bidsList"] if ok else None

    def get_bbo(self, symbol: str, exchange: str) -> Tuple:
        ok, key, data = self.databox.get_md(exchange, self.inst_type_str, "DEPTH1", symbol)
        return data["asksList"][0], data["bidsList"][0] if ok else None

    def get_trades(self, symbol: str, exchange: str) -> Tuple:
        ok, key, data = self.databox.get_md(exchange, self.inst_type_str, "TRADES", symbol, length=1)
        return data[0]["px"] if ok else None

    # def get_bbo(self, req: SubscribeRequest) -> Tuple:
    #     ok, key, data = self.databox.get_md(req.exchange.value, self.inst_type_str, "DEPTH1", req.symbol)
    #     return data["asksList"][0], data["bidsList"][0] if ok else None
    #
    # def get_trades(self, req: SubscribeRequest) -> str:
    #     ok, key, data = self.databox.get_md(req.exchange.value, self.inst_type_str, "TRADES", req.symbol, length=1)
    #     return data[0]["px"] if ok else None

    def on_hft_orderTrade(self, hft_orderTrade: OrderTrade):
        print(hft_orderTrade)
        if hft_orderTrade.strategyId != self.strategyid:
            return

        if hft_orderTrade.instId not in self.coin_list:
            return

        order_trade = self.gw_converter.convert_order_trade(hft_orderTrade)
        # self.write_log(f"[OrderTrade]: {order_trade.to_dict()}")
        # print(order_trade)
        # print(order_trade)
        self.on_ordertrade(order_trade)
        return

    def on_hft_balance(self, hft_balance):
        # if self.account_recorded:
        #     return

        if hft_balance.strategyId != self.strategyid:
            return

        if hft_balance.currency not in self.balance_codes:
            return

        if hft_balance.instTypeEnum != SPOT:
            return
        # print(hft_balance)
        account = self.gw_converter.convert_account(hft_balance)
        if account.exchange == Exchange.BTSE:
            self.on_account(account)
        # if account.exchange == Exchange.BINANCE:
        #     self.on_account(account)
        # if account.exchange == Exchange.BINANCE:
        #     self.binance_account = account.total + account.pnl
        # elif account.exchange == Exchange.GATEIO:
        #     self.gate_account = account.balance
        # elif account.exchange == Exchange.BYBIT:
        #     self.bybit_account = account.available + account.pnl
        # elif account.exchange == Exchange.BTSE:
        #     self.btse_account = account.available + account.pnl

        # if self.binance_account and self.btse_account:
        #     v = [datetime.now().strftime('%Y-%m-%d'), self.binance_account, self.btse_account,
        #          self.binance_account + self.btse_account]
        #     self.Account_R.record(v, self.account_csv_header)
        #     self.write_log(f"bnbbt_account@{self.binance_account + self.bybit_account}")
        #     self.account_recorded = True

    def on_hft_position(self, hft_position):
        if hft_position.strategyId != self.strategyid:
            return
        if hft_position.instId not in self.coin_list:
            return
        position = self.gw_converter.convert_position(hft_position)
        self.on_position(position)
        # self.write_log(f"[Position]: {position.to_dict()}")
        self.status.update({'con': True})

    def query_smc(self, exchange: Exchange, instrument: str):
        ok, instrument_info = self.smc.get_instrument_info(exchange.value, self.inst_type, instrument)
        # ok, all_intrument_info_list = smc.get_all_instruments()
        print(ok)
        print(instrument_info)

    def cancel_all_orders(self, cancel_all_request: CancelAllRequest):
        self.tradeClient.cancel_all_orders(exchangeType=Exchange_VT2HFT[cancel_all_request.exchange],
                                           instType=ProductType_VT2HFT[cancel_all_request.product],
                                           strategyId=self.strategyid,
                                           instId=cancel_all_request.symbol)

    def query_open_orders(self, query_all_request: QueryOpenOrderRequest):
        self.tradeClient.query_open_orders(exchangeType=Exchange_VT2HFT[query_all_request.exchange],
                                           instType=ProductType_VT2HFT[query_all_request.product],
                                           strategyId=self.strategyid)


import signal

hft_setting = {
    "redis_host": "localhost",
    "redis_port": 8379,
    "redis_password": "refZa2KcTj$QjyXk",
    "oms_tcmd": "tcp://127.0.0.1:5555",
    "oms_rcmd": "tcp://127.0.0.1:7777",
    "inst_type": "SPOT",
    "subscribe_codes": [
        "BTSE.SPOT.ETH-USDT.DEPTH1",
        "BTSE.SPOT.ETH-USDT.TRADES"
        # "BTSE.SPOT.BTSE-USDT.DEPTH20",
        # "BTSE.SPOT.BTSE-USDT.TRADES",
        # "BTSE.SPOT.BTSE-USDT.DEPTH1"
        # "UNISWAP.SPOT.BTSE-ETH.DEPTH1"

    ],
    "balance_codes": ["BTSE", "USDT"],
    "timer_interval": 1000,
    "strategyId": "yn_ts03",
    "account_path": "/home/yinan.chen/bzbn/mt/bzbn_account.csv"
}
event_engine = EventEngine()
hft = HftGateway(event_engine)
hft.connect(hft_setting)
cancel_all_request = CancelAllRequest(symbol="BTSE-USDT", exchange=Exchange.BTSE, product=Product.SPOT)
hft.cancel_all_orders(cancel_all_request)

# time.sleep(10)
# queryRequest = QueryOpenOrderRequest(symbol="BTSE-USDT", exchange=Exchange.BTSE, product=Product.SPOT)
# hft.query_open_orders(queryRequest)

pid = os.getpid()
os.kill(pid, signal.SIGKILL)
# req1 = SubscribeRequest(symbol="BTSE-USDT", exchange=Exchange.BTSE)
# req2 = SubscribeRequest(symbol="BTSE-ETH", exchange=Exchange.UNISWAP)
# req3 = SubscribeRequest(symbol="ETH-USDT", exchange=Exchange.BTSE)
# hft.subscribe(req1)
# hft.subscribe(req2)
# hft.subscribe(req3)
# # ask, bid = hft.get_bbo(req2)
# ask,  bid = hft.get_spot_order_book(symbol="BTSE-USDT", exchange="BTSE")
#
# print(ask)
# print(bid)
# res = hft.get_trades(req3)
# print(res)
# print(type(res))
# req = SubscribeRequest(symbol='BTC-USDT', exchange=Exchange.BTSE)
# hft.subscribe(req)
# req = SubscribeRequest(symbol='BTC-USDT', exchange=Exchange.BINANCE)
# hft.subscribe(req)

# hft.query_position()
# hft.query_smc(Exchange.BINANCE, instrument="ORDI-USDT")
# res = hft.get_funding_rate(req)
# req = SubscribeRequest(symbol='ETH-USDT', exchange=Exchange.GATEIO)
# hft.subscribe(req)
# time.sleep(2)
# time.sleep(3)
# order_req = OrderRequest(symbol='BTSE-USDT',
#                          exchange=Exchange.BTSE,
#                          direction=Direction.SHORT,
#                          type=OrderType.MAKER,
#                          volume=1000,
#                          price=1,
#                          offset=Offset.OPEN,
#                          reduce_only=False,
#                          reference="testreference"
#                          )
# id = hft.send_order(order_req)
# print(id)

# cancel_req = CancelRequest(orderid="1717043956143121965", symbol="BTSE-USDT", exchange=Exchange.BTSE)
# hft.cancel_order(cancel_req)
# hft.query_order(cancel_req)
